package cn.stimd.spring.context.annotation;

import cn.stimd.spring.beans.factory.config.BeanDefinition;
import cn.stimd.spring.beans.factory.config.BeanDefinitionHolder;
import cn.stimd.spring.beans.factory.config.ConfigurableBeanFactory;
import cn.stimd.spring.beans.factory.support.BeanDefinitionRegistry;
import cn.stimd.spring.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import cn.stimd.spring.context.EnvironmentAware;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReaderFactory;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * 负责处理@Configuration声明的配置类
 * <p>
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {
    private final Log logger = LogFactory.getLog(getClass());
    private Environment environment;
    private ResourceLoader resourceLoader = new DefaultResourceLoader();
    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
    private ConfigurationClassBeanDefinitionReader reader;

    /**
     * 处理配置类的唯一入口，在AbstractApplicationContext的refresh方法中step-5（invokeBeanFactoryPostProcessors）中被调用
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws RuntimeException {
        List<BeanDefinitionHolder> configCandidates = new ArrayList<>();

        //1. 寻找容器中已存在的配置类
        //Notice: 此时存在的配置类是预先指定的，为了便于区分，称之为引导配置类。在step-2中通过对引导配置类的解析，
        //以组件扫描或import等方式加载其他的配置类。
        String[] names = registry.getBeanDefinitionNames();
        for (String name : names) {
            BeanDefinition definition = registry.getBeanDefinition(name);
            //判断的依据为是否使用@Configuration等注解声明，如果是进一步区分full模式和lite模式
            if(ConfigurationClassUtils.checkConfigurationClassCandidate(definition, this.metadataReaderFactory)){
                configCandidates.add(new BeanDefinitionHolder(definition, name));
                logger.info("[Context] [加载配置类] --> beanName: " + name);
            }
        }

        if(configCandidates.isEmpty()){
            return;
        }

        //2. 解析引导配置类，加载所有的配置类
        Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
        ConfigurationClassParser parser = new ConfigurationClassParser(metadataReaderFactory, environment, resourceLoader, registry);
        parser.parse(candidates);

        //3. 从所有的配置类中提取BeanDefinition
        if(this.reader == null){
            this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.resourceLoader, this.environment);
        }
        this.reader.loadBeanDefinitions(parser.getConfigurationClasses());
    }

    @Override
    public void postProcessBeanFactory(ConfigurableBeanFactory beanFactory) throws RuntimeException{
        //为Full模式的配置类创建代理，BeanMethod的入参中调用了另一个BeanMethod，返回相应Bean的引用，而不是创建新实例
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}
